home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / mktty / mktty.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-06-15  |  12.0 KB  |  494 lines

  1. /* 
  2.  * mktty.c --
  3.  *
  4.  *    Main program for the "mktty" program.  Provides a linkup between
  5.  *    a pseudo-device with an attached tty driver and a raw terminal
  6.  *    device.  Also allows a network TCP connection to be used in
  7.  *    place of the raw terminal device.
  8.  *
  9.  * Copyright 1987, 1988 Regents of the University of California
  10.  * Permission to use, copy, modify, and distribute this
  11.  * software and its documentation for any purpose and without
  12.  * fee is hereby granted, provided that the above copyright
  13.  * notice appear in all copies.  The University of California
  14.  * makes no representations about the suitability of this
  15.  * software for any purpose.  It is provided "as is" without
  16.  * express or implied warranty.
  17.  */
  18.  
  19. #ifndef lint
  20. static char rcsid[] = "$Header: /a/newcmds/mktty/RCS/mktty.c,v 1.2 89/06/15 10:50:36 ouster Exp $ SPRITE (Berkeley)";
  21. #endif not lint
  22.  
  23. #include <sprite.h>
  24. #include <bstring.h>
  25. #include <errno.h>
  26. #include <fcntl.h>
  27. #include <fs.h>
  28. #include <netdb.h>
  29. #include <pdev.h>
  30. #include <sgtty.h>
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <string.h>
  34. #include <sys/file.h>
  35. #include <sys/types.h>
  36. #include <sys/socket.h>
  37. #include <netinet/in.h>
  38. #include <td.h>
  39.  
  40. /*
  41.  * Library imports:
  42.  */
  43.  
  44. extern char *getenv();
  45.  
  46. /*
  47.  * Tokens for the terminal driver, and for the pseudo-device attached
  48.  * to it.
  49.  */
  50.  
  51. static Td_Pdev        pdev;
  52. static Td_Terminal    term;
  53.  
  54. /*
  55.  * Information about the device (the end that connects to the raw
  56.  * RS232 line).
  57.  */
  58.  
  59. int rawInputID;            /* Sprite stream ID used for reading
  60.                  * raw tty. */
  61. int rawOutputID;        /* Sprite stream ID used for writing
  62.                  * raw tty. */
  63. struct sgttyb params;        /* Saved mode bits for device. */
  64. int ttyGroup;            /* Saved controlling group for tty. */
  65.  
  66. /*
  67.  * Buffer used to hold characters waiting to be output.  This is needed
  68.  * because when we retrieve characters from the terminal driver we don't
  69.  * know how many can actually be output to the terminal itself.
  70.  */
  71.  
  72. #define OUT_BUF_SIZE 100
  73. char outputBuffer[OUT_BUF_SIZE];
  74. char *nextOutput;        /* Location of next char. to be output. */
  75. int outputCount = 0;        /* Number of chars. left to output. */
  76. int outputHandler = 0;        /* Non-zero means Fs_Dispatch handler exists
  77.                  * for rawOutputID. */
  78.  
  79. /*
  80.  * Miscellaneous.
  81.  */
  82.  
  83. int exitValue = 0;            /* Exit status for program. */
  84.  
  85. /*
  86.  * Forward references to procedures defined in this file:
  87.  */
  88.  
  89. extern int    Cleanup();
  90. extern int    RawControlProc();
  91. extern void    RawInputProc();
  92. extern void    RawOutputProc();
  93.  
  94. /*
  95.  *----------------------------------------------------------------------
  96.  *
  97.  * main --
  98.  *
  99.  *    Main program for mktty.  Initializes tty setup, waits for things
  100.  *    to happen, calls appropriate handlers.
  101.  *
  102.  * Results:
  103.  *    None.
  104.  *
  105.  * Side effects:
  106.  *    Lots.  See the man page.
  107.  *
  108.  *----------------------------------------------------------------------
  109.  */
  110.  
  111. main(argc, argv)
  112.     int argc;            /* Number of arguments. */
  113.     char **argv;        /* Arguments.  See man page for details. */
  114. {
  115.     char *pdevName, *devName;
  116.     char **args = NULL;
  117.     char *colonPtr;
  118.     int i;
  119.     int applPid;
  120.  
  121.     /*
  122.      *------------------------------
  123.      * Parse command line arguments.
  124.      *------------------------------
  125.      */
  126.  
  127.     if (argc < 2) {
  128.     fprintf(stderr, "Usage: %s device pdev [command]\n", argv[0]);
  129.     Cleanup();
  130.     }
  131.     devName = argv[1];
  132.     pdevName = argv[2];
  133.     if (argc > 3) {
  134.     args = &argv[3];
  135.     }
  136.  
  137.     /*
  138.      *------------------------------------------------------
  139.      * Open the connection to the raw terminal or network.
  140.      *------------------------------------------------------
  141.      */
  142.  
  143.     colonPtr = index(devName, ':');
  144.     if (colonPtr == NULL) {
  145.     if (strcmp(devName, "-") == 0) {
  146.         /*
  147.          * Use standard input and output for the terminal device.
  148.          */
  149.         rawInputID = 0;
  150.         rawOutputID = 1;
  151.     } else {
  152.         /*
  153.          * This is a terminal device.
  154.          */
  155.  
  156.         rawInputID = open(devName, O_RDWR, 0);
  157.         if (rawInputID < 0) {
  158.         fprintf(stderr, "Mktty couldn't open \"%s\": %s.\n",
  159.             devName, strerror(errno));
  160.         exit(1);
  161.         }
  162.         rawOutputID = dup(rawInputID);
  163.     }
  164.     if (ioctl(rawInputID, TIOCGETP, (char *) ¶ms) != 0) {
  165.         fprintf(stderr, "Mktty: \"%s\" isn't a tty.\n", devName);
  166.         exit(1);
  167.     }
  168.     if (ioctl(rawInputID, TIOCGPGRP, (char *) &ttyGroup) != 0) {
  169.         fprintf(stderr, "Mktty couldn't read controlling group: %s\n",
  170.             strerror(errno));
  171.         exit(1);
  172.     }
  173.     i = params.sg_flags;
  174.     params.sg_flags = RAW;
  175.     if (ioctl(rawInputID, TIOCSETP, (char *) ¶ms) != 0) {
  176.         terminalSetupError:
  177.         fprintf(stderr, "Mktty couldn't reset terminal state: %s\n",
  178.             strerror(errno));
  179.         exit(1);
  180.     }
  181.     params.sg_flags = i;
  182.     i = getpgrp(0);
  183.     if (ioctl(rawInputID, TIOCSPGRP, (char *) &i) != 0) {
  184.         goto terminalSetupError;
  185.     }
  186.     if ((fcntl(rawInputID, F_SETFL, FNDELAY) == -1)
  187.         || (fcntl(rawOutputID, F_SETFL, FNDELAY) == -1)) {
  188.         fprintf(stderr, "%s non-blocking mode: %s\n",
  189.             "Mktty couldn't put raw device into",
  190.             strerror(errno));
  191.         exit(1);
  192.     }
  193.     } else {
  194.     /*
  195.      * Network TCP connection:  "host:portNum"
  196.      */
  197.  
  198.     struct hostent *hostPtr;
  199.     struct sockaddr_in serverAddress;
  200.  
  201.     *colonPtr = 0;
  202.     rawInputID = socket(AF_INET, SOCK_STREAM, 0);
  203.     if (rawInputID < 0) {
  204.         perror("Mktty couldn't open socket: %s", strerror(errno));
  205.         exitValue = 1;
  206.         Cleanup();
  207.     }
  208.     rawOutputID = dup(rawInputID);
  209.     bzero((char *) &serverAddress, sizeof(serverAddress));
  210.     serverAddress.sin_family = AF_INET;
  211.     serverAddress.sin_port = htons(atoi(colonPtr+1));
  212.     hostPtr = gethostbyname(devName);
  213.     if (hostPtr == NULL) {
  214.         fprintf(stderr, "Mktty couldn't find a host named \"%s\"\n",
  215.             devName);
  216.         exitValue = 1;
  217.         Cleanup();
  218.     }
  219.     bcopy((char *) hostPtr->h_addr, (char *) &serverAddress.sin_addr,
  220.         sizeof(serverAddress.sin_addr));
  221.     if (connect(rawInputID, (struct sockaddr *) &serverAddress,
  222.         sizeof(serverAddress)) != 0) {
  223.         perror("Mktty couldn't connect to server");
  224.         fprintf(stderr, "This probably means you didn't start a server");
  225.         fprintf(stderr, " process\nor typed the wrong port number\n"); 
  226.         exitValue = 1;
  227.         Cleanup();
  228.     }
  229.     }
  230.  
  231.     /*
  232.      *------------------------------------------------------
  233.      * Set up the pseudo-terminal device.
  234.      *------------------------------------------------------
  235.      */
  236.  
  237.     pdev = Td_CreatePdev(pdevName, (char **) NULL, &term, RawControlProc,
  238.         (ClientData) 0);
  239.     if (pdev == NULL) {
  240.     fprintf(stderr, "Mktty couldn't open pseudo-device \"%s\": %s.\n",
  241.         pdevName, pdev_ErrorMsg);
  242.     exitValue = 1;
  243.     Cleanup();
  244.     }
  245.  
  246.     /*
  247.      *--------------------------------------------------
  248.      * Spawn the application process, if one is desired.
  249.      *--------------------------------------------------
  250.      */
  251.  
  252.     if (args != NULL) {
  253.     Sig_Action old, new;
  254.  
  255.     new.action = SIG_HANDLE_ACTION;
  256.     new.handler = Cleanup;
  257.     new.sigHoldMask = 0;
  258.     Sig_SetAction(SIG_CHILD, &new, &old);
  259.     applPid = fork();
  260.     if (applPid == -1) {
  261.         fprintf(stderr, "Mktty couldn't fork application: %s\n.",
  262.         strerror(errno));
  263.         exitValue = 1;
  264.         Cleanup();
  265.     }
  266.     if (applPid == 0) {
  267.         int ttyID, pid;
  268.         FILE *f;
  269.         char *tty;
  270.  
  271.         tty = getenv("TTY");        /* May be needed below. */
  272.         setenv("TTY", pdevName);
  273.  
  274.         ttyID = open(pdevName, O_RDWR, 0);
  275.         if (ttyID < 0) {
  276.         fprintf(stderr,
  277.             "Mktty couldn't open application end of terminal (%s).\n",
  278.             strerror(errno));
  279.         _exit(1);
  280.         }
  281.         pid = getpid();
  282.         if (setpgrp(0, pid) == -1) {
  283.         fprintf(stderr,
  284.             "Mktty couldn't set process family ID (%s).\n",
  285.             strerror(errno));
  286.         _exit(1);
  287.         }
  288.         if (ioctl(ttyID, TIOCSPGRP, (char *) &pid) != 0) {
  289.         fprintf(stderr,
  290.             "Mktty couldn't set process family for terminal (%s).\n",
  291.             strerror(errno));
  292.         _exit(1);
  293.         }
  294.         dup2(ttyID, 0);
  295.         dup2(ttyID, 1);
  296.         dup2(ttyID, 2);
  297.         for (i = 3; i <= ttyID; i++) {
  298.         close(i);
  299.         }
  300.         execvp(args[0], args);
  301.         if (tty == NULL) {
  302.         _exit(1);
  303.         }
  304.         f = fopen(tty, "w");
  305.         if (f != NULL) {
  306.         fprintf(f, "Couldn't execute \"%s\":\n", args[0],
  307.             strerror(errno));
  308.         }
  309.         _exit(1);
  310.     }
  311.     }
  312.  
  313.     /*
  314.      *--------------------------------------------
  315.      * Enter a loop reading and processing events.
  316.      *--------------------------------------------
  317.      */
  318.  
  319.     Fs_EventHandlerCreate(rawInputID, FS_READABLE, RawInputProc,
  320.         (ClientData) 0);
  321.     while (TRUE) {
  322.     if (outputCount > 0) {
  323.         if (!outputHandler) {
  324.         Fs_EventHandlerCreate(rawOutputID, FS_WRITABLE,
  325.             RawOutputProc, (ClientData) 0);
  326.         outputHandler = 1;
  327.         }
  328.     } else {
  329.         if (outputHandler) {
  330.         Fs_EventHandlerDestroy(rawOutputID);
  331.         outputHandler = 0;
  332.         }
  333.     }
  334.     Fs_Dispatch();
  335.     }
  336. }
  337.  
  338. /*
  339.  *----------------------------------------------------------------------
  340.  *
  341.  * RawInputProc --
  342.  *
  343.  *    This procedure is called by Fs_Dispatch whenever data becomes
  344.  *    readable from the raw tty.  This procedure reads the data
  345.  *    and passes it to the terminal driver.
  346.  *
  347.  * Results:
  348.  *    None.
  349.  *
  350.  * Side effects:
  351.  *    Whatever happens in the terminal driver.
  352.  *
  353.  *----------------------------------------------------------------------
  354.  */
  355.  
  356. void
  357. RawInputProc()
  358. {
  359. #define INPUT_SIZE 100
  360.     char input[INPUT_SIZE];
  361.     int numBytes;
  362.  
  363.     numBytes = read(rawInputID, input, INPUT_SIZE);
  364.     if (numBytes < 0) {
  365.     if (errno == EWOULDBLOCK) {
  366.         return;
  367.     }
  368.     fprintf(stderr, "Mktty couldn't read from device: %s\n",
  369.         strerror(errno));
  370.     exit(1);
  371.     }
  372.     if (numBytes == 0) {
  373.     fprintf(stderr, "Mktty received end-of-file from device.\n");
  374.     fprintf(stderr, "Perhaps network connection was closed or refused?\n");
  375.     exit(1);
  376.     }
  377.     Td_PutRaw(term, numBytes, input);
  378. }
  379.  
  380. /*
  381.  *----------------------------------------------------------------------
  382.  *
  383.  * RawOutputProc --
  384.  *
  385.  *    This procedure is invoked by Fs_Dispatch when the raw tty
  386.  *    becomes writable and there is data in the terminal's output
  387.  *    buffer waiting to be written.
  388.  *
  389.  * Results:
  390.  *    None.
  391.  *
  392.  * Side effects:
  393.  *    Data gets written to the terminal.
  394.  *
  395.  *----------------------------------------------------------------------
  396.  */
  397.  
  398. void
  399. RawOutputProc()
  400. {
  401.     int count;
  402.  
  403.     if (outputCount == 0) {
  404.     return;
  405.     }
  406.     count = write(rawOutputID, nextOutput, outputCount);
  407.     if (count < 0) {
  408.     if (errno != EWOULDBLOCK) {
  409.         fprintf(stderr, "Error writing raw tty: %s.\n", strerror(errno));
  410.         Cleanup();
  411.     }
  412.     return;
  413.     }
  414.     nextOutput += count;
  415.     outputCount -= count;
  416.     if (outputCount == 0) {
  417.     outputCount = Td_GetRaw(term, OUT_BUF_SIZE, outputBuffer);
  418.     nextOutput = outputBuffer;
  419.     }
  420. }
  421.  
  422. /*
  423.  *----------------------------------------------------------------------
  424.  *
  425.  * RawControlProc --
  426.  *
  427.  *    Invoked by the terminal driver to perform control operations
  428.  *    on the raw tty device.
  429.  *
  430.  * Results:
  431.  *    Always returns 0.
  432.  *
  433.  * Side effects:
  434.  *    Varies, depending on the operation.
  435.  *
  436.  *----------------------------------------------------------------------
  437.  */
  438.  
  439.     /* ARGSUSED */
  440. int
  441. RawControlProc(clientData, command, inSize, inBuffer, outSize, outBuffer)
  442.     ClientData clientData;    /* Not used. */
  443.     int command;        /* Identifies control operation being
  444.                  * invoked, e.g. TD_COOKED_SIGNAL. */
  445.     int inSize;            /* Number of bytes of input data available
  446.                  * to us. */
  447.     char *inBuffer;        /* Pointer to input data. */
  448.     int outSize;        /* Maximum number of bytes of output data
  449.                  * we can return to caller. */
  450.     char *outBuffer;        /* Area in which to store output data for
  451.                  * caller. */
  452. {
  453.     switch (command) {
  454.     case TD_RAW_OUTPUT_READY:
  455.         if (outputCount == 0) {
  456.         outputCount = Td_GetRaw(term, OUT_BUF_SIZE, outputBuffer);
  457.         nextOutput = outputBuffer;
  458.         }
  459.         break;
  460.     case TD_RAW_FLUSH_OUTPUT:
  461.         outputCount = 0;
  462.         break;
  463.     }
  464.     return 0;
  465. }
  466.  
  467. /*
  468.  *----------------------------------------------------------------------
  469.  *
  470.  * Cleanup --
  471.  *
  472.  *    This procedure is invoked to restore the terminal state and
  473.  *    exit.  It is called as a signal handler when the child dies,
  474.  *    or at other times when errors occur.
  475.  *
  476.  * Results:
  477.  *    None.
  478.  *
  479.  * Side effects:
  480.  *    This process dies.  The terminal's state gets restored.
  481.  *
  482.  *----------------------------------------------------------------------
  483.  */
  484.  
  485. Cleanup()
  486. {
  487.     if (pdev != NULL) {
  488.     Td_DeletePdev(pdev);
  489.     }
  490.     ioctl(rawInputID, TIOCSETP, (char *) ¶ms);
  491.     ioctl(rawInputID, TIOCSPGRP, (char *) &ttyGroup);
  492.     exit(exitValue);
  493. }
  494.